﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;

namespace NodeExpander
{
    public partial class ExpanderControl : UserControl, IExpanderSlot
    {
        private List<IExpanderSlot> slots;
        private List<FrameworkElement> controlSlots;

        public bool CanAddSlots { get; set; }
        public bool ReadOnly { get; set; }
        public bool ForceSave { get; set; }

        public event EventHandler OnAddSlot;
        public event EventHandler OnSlotFocus;
        public event EventHandler OnSave;
        public event EventHandler OnCancel;
        public event EventHandler OnClose;

        public int SlotIndex { get; set; }
        public string SlotText { get; set; }
        public bool AutoShow { get; set; }
        public bool IsRequired { get; set; }
        public FrameworkElement InputView { get; set; }
        public FrameworkElement DescriptionView { get; set; }
        public IExpanderSlot PreviousSlot { get; set; }
        public IExpanderSlot NextSlot { get; set; }

        private bool animComplete;

        public ExpanderControl()
        {
            InitializeComponent();
        }

        public List<IExpanderSlot> Slots
        {
            get
            {
                if (slots == null)
                {
                    slots = new List<IExpanderSlot>();
                }

                return slots;
            }

            set
            {
                slots = value;
            }
        }

        private List<FrameworkElement> ControlSlots
        {
            get
            {
                if (controlSlots == null)
                {
                    controlSlots = new List<FrameworkElement>();
                }

                return controlSlots;
            }
            set
            {
                controlSlots = value;
            }
        }


        #region Fire Events

        protected void FireOnAddSlot(IExpanderSlot slot)
        {
            if (OnAddSlot != null)
            {
                OnAddSlot(this, new ExpanderSlotEventArgs(slot));
            }
        }

        protected void FireOnSlotFocus(IExpanderSlot slot)
        {
            if (OnSlotFocus != null)
            {
                OnSlotFocus(this, new ExpanderSlotEventArgs(slot));
            }
        }

        protected void FireOnSave(IExpanderSlot slot)
        {
            if (OnSave != null)
            {
                OnSave(this, new ExpanderSlotEventArgs(slot));
            }
        }

        protected void FireOnCancel(IExpanderSlot slot)
        {
            if (OnCancel != null)
            {
                OnCancel(this, new ExpanderSlotEventArgs(slot));
            }
        }

        protected void FireOnClose(IExpanderSlot slot)
        {
            if (OnClose != null)
            {
                OnClose(this, new ExpanderSlotEventArgs(slot));
            }
        }

        #endregion

        #region Animations

        private void animShiftUp(FrameworkElement slot, int count)
        {
            var storyboard = new Storyboard();
            var animMotion = new DoubleAnimation();

            Double animDistance = 25.00 * count;
            Double animSpeed = 0.5;
            Double y = Canvas.GetTop(slot);

            animMotion.Duration = new Duration(TimeSpan.FromSeconds(animSpeed));

            animMotion.From = y;
            animMotion.To = y - animDistance;

            Storyboard.SetTargetProperty(animMotion, new PropertyPath(Canvas.TopProperty));

            Storyboard.SetTarget(animMotion, slot);

            storyboard.Children.Add(animMotion);

            animComplete = false;

            storyboard.Completed += (sender, e) =>
            {
                animComplete = true;
            };

            storyboard.Begin();
        }

        private void animShiftDown(FrameworkElement slot)
        {
            var storyboard = new Storyboard();
            var animMotion = new DoubleAnimation();

            Double animDistance = 25.00;
            Double animSpeed = 0.5;
            Double y = Canvas.GetTop(slot);

            animMotion.Duration = new Duration(TimeSpan.FromSeconds(animSpeed));

            animMotion.From = y;
            animMotion.To = y + animDistance;

            Storyboard.SetTargetProperty(animMotion, new PropertyPath(Canvas.TopProperty));

            Storyboard.SetTarget(animMotion, slot);

            storyboard.Children.Add(animMotion);

            animComplete = false;

            storyboard.Completed += (sender, e) =>
            {
                animComplete = true;
            };

            storyboard.Begin();
            
        }

        private void animShiftLeft(FrameworkElement slot)
        {
            var storyboard = new Storyboard();
            var animMotion = new DoubleAnimation();
            var animOpacity = new DoubleAnimation();

            Double animDistance = 100.00;
            Double animSpeed = 0.5;
            Double x = Canvas.GetLeft(slot);

            animMotion.Duration = new Duration(TimeSpan.FromSeconds(animSpeed));
            animOpacity.Duration = new Duration(TimeSpan.FromSeconds(animSpeed));

            animMotion.From = x + animDistance;
            animMotion.To = x;

            animOpacity.From = 0;
            animOpacity.To = 100;

            Storyboard.SetTargetProperty(animMotion, new PropertyPath(Canvas.LeftProperty));
            Storyboard.SetTarget(animMotion, slot);

            Storyboard.SetTargetProperty(animOpacity, new PropertyPath("(FrameworkElement.Opacity)"));
            Storyboard.SetTarget(animOpacity, slot);

            storyboard.Children.Add(animMotion);
            storyboard.Children.Add(animOpacity);

            animComplete = false;

            storyboard.Completed += (sender, e) =>
            {
                animComplete = true;
            };

            storyboard.Begin();
        }

        private void animShiftRight(FrameworkElement slot)
        {
            var storyboard = new Storyboard();
            var animMotion = new DoubleAnimation();
            var animOpacity = new DoubleAnimation();

            Double animDistance = 100.00;
            Double animSpeed = 0.5;
            Double x = Canvas.GetLeft(slot);

            animMotion.Duration = new Duration(TimeSpan.FromSeconds(animSpeed));
            animOpacity.Duration = new Duration(TimeSpan.FromSeconds(animSpeed));

            animMotion.From = x;
            animMotion.To = x + animDistance;

            animOpacity.From = 100;
            animOpacity.To = 0;

            Storyboard.SetTargetProperty(animMotion, new PropertyPath(Canvas.LeftProperty));
            Storyboard.SetTarget(animMotion, slot);

            Storyboard.SetTargetProperty(animOpacity, new PropertyPath("(FrameworkElement.Opacity)"));
            Storyboard.SetTarget(animOpacity, slot);

            storyboard.Children.Add(animMotion);
            storyboard.Children.Add(animOpacity);

            animComplete = false;

            storyboard.Completed += (sender, e) =>
            {
                animComplete = true;
            };

            storyboard.Begin();
        }

        #endregion

        private void ExpandNode_Expanded(object sender, RoutedEventArgs e)
        {
            var slotCount = controlSlots.Count();
            if (slotCount == 0)
            {
                AddSlot(controlSlots.First());
            }
        }

        private void ExpandNodeMore_Expanded(object sender, RoutedEventArgs e)
        {
            if (animComplete != false)
            {
                var slotCount = AnimationCanvas.Children.Count();
            }
            ExpandNodeMore.IsExpanded = false;
        }

        public void AddSlot(FrameworkElement slot)
        {   
            foreach (FrameworkElement child in AnimationCanvas.Children)
            {
                animShiftDown(child);
            }

            AnimationCanvas.Children.Add(slot);
            animShiftLeft(slot);
        }

        private void RemoveSlot(int count)
        {
            for (int i = count; i > 0; i--)
            {
                var topSlot = AnimationCanvas.Children.Last() as FrameworkElement;
                
                AnimationCanvas.Children.Remove(topSlot);
 
                foreach (FrameworkElement child in AnimationCanvas.Children)
                {
                    animShiftUp(child, count);
                }
            }
        }

        public void BuildSlot(IExpanderSlot slot)
        {
            var slotIndex = slot.SlotIndex;
            var slotDescription = slot.DescriptionView;
            var slotInput = slot.InputView;

            #region Slot FrameworkElement

            Grid slotGrid = new Grid();

            slotGrid.Background = new SolidColorBrush(Colors.LightGray);

            ColumnDefinition cd1 = new ColumnDefinition();
            cd1.Width = new GridLength(125);

            ColumnDefinition cd2 = new ColumnDefinition();
            cd2.Width = new GridLength(125);

            ColumnDefinition cd3 = new ColumnDefinition();
            cd3.Width = new GridLength(25);

            slotGrid.ColumnDefinitions.Add(cd1);
            slotGrid.ColumnDefinitions.Add(cd2);
            slotGrid.ColumnDefinitions.Add(cd3);

            Expander slotRemove = new Expander
            {
                ExpandDirection = ExpandDirection.Left,
            };

            slotRemove.Expanded += (sender2, e2) =>
            {
                var slotCount = controlSlots.Count();
                var slotRemoveCount = slotCount - slotIndex;

                if (animComplete != false)
                {
                    if (slotRemoveCount == 0)
                    {
                        RemoveSlot(1);
                    }
                    else
                    {
                        RemoveSlot(slotRemoveCount);
                    }
                }

                slotRemove.IsExpanded = false;
            };

            slotDescription.SetValue(Grid.ColumnProperty, 0);
            slotInput.SetValue(Grid.ColumnProperty, 1);
            slotRemove.SetValue(Grid.ColumnProperty, 1);

            #endregion

            slotGrid.Children.Add(slotDescription);
            slotGrid.Children.Add(slotInput);
            slotGrid.Children.Add(slotRemove);
            slotGrid.Tag = slotIndex;

            controlSlots.Add(slotGrid);
        }

    }
}
